Skip to content

fix(vue-query): allow computed ref as queryKey property in queryOptions#10530

Open
KimHyeongRae0 wants to merge 1 commit intoTanStack:mainfrom
KimHyeongRae0:fix/vue-query-options-query-key-reactive
Open

fix(vue-query): allow computed ref as queryKey property in queryOptions#10530
KimHyeongRae0 wants to merge 1 commit intoTanStack:mainfrom
KimHyeongRae0:fix/vue-query-options-query-key-reactive

Conversation

@KimHyeongRae0
Copy link
Copy Markdown

@KimHyeongRae0 KimHyeongRae0 commented Apr 20, 2026

🎯 Changes

Closes #10525.

Fixes a regression from #10452 where the queryOptions helper in @tanstack/vue-query stopped accepting computed refs, Ref values, or other reactive values for the queryKey property. The earlier fix in #10465 only covered the enabled property, so user code like

queryOptions({
  queryKey: computed(() => ['foo', toValue(id)] as const),
  queryFn: async () => /* ... */,
})

was emitting TS2769: No overload matches this call on every version since 5.98.0.

This PR narrows the fix to the queryKey property only (mirroring how #10465 special-cased enabled), so downstream usages like queryClient.fetchQuery(options) — which depend on non-reactive core types for other properties — continue to work without regressions.

Type tests added for both computed(...) and ref(...) as queryKey in packages/vue-query/src/__tests__/queryOptions.test-d.ts.

✅ Checklist

  • I have followed the steps in the Contributing guide.
  • I have tested this code locally with npx nx run @tanstack/vue-query:test:types and the full pnpm --filter @tanstack/vue-query test suite (291 tests passed, no type errors).

🚀 Release Impact

  • This change affects published code, and I have generated a changeset.
  • This change is docs/CI/dev-only (no release).

Summary by CodeRabbit

Release Notes

  • Bug Fixes
    • queryOptions.queryKey now accepts reactive values (Ref, ComputedRef) in addition to plain arrays, restoring previously supported functionality.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 20, 2026

📝 Walkthrough

Walkthrough

This pull request fixes a regression in @tanstack/vue-query where the queryOptions.queryKey property now properly accepts Vue reactive forms (such as Ref<QueryKey> and ComputedRef<QueryKey>) alongside plain QueryKey arrays. The fix is implemented through type system updates and corresponding type-level tests.

Changes

Cohort / File(s) Summary
Release Documentation
.changeset/fix-vue-query-options-query-key-reactive.md
Added patch-level release note documenting the fix allowing queryOptions.queryKey to accept Vue reactive forms (Ref/ComputedRef) alongside plain arrays.
Type Tests
packages/vue-query/src/__tests__/queryOptions.test-d.ts
Added two new type-level test cases verifying queryKey accepts computed returning tuple keys and ref holding tuple keys directly.
Type Definition
packages/vue-query/src/queryOptions.ts
Updated QueryOptions mapped type to special-case queryKey as MaybeRef<TQueryKey>, enabling reactive form support while preserving existing property typing for other options.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 With whiskers twitching, code so bright,
A rabbit crafts a type-fix right,
queryKey now flows reactive and free,
Ref and Computed, wild with glee,
Vue's magic dances in harmony! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: allowing computed refs as queryKey in queryOptions, which directly addresses the regression fix documented in the PR description.
Description check ✅ Passed The description includes all required sections: detailed changes explaining the regression and fix, completed checklist items confirming local testing and changeset generation, and clear release impact declaration.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/vue-query/src/__tests__/queryOptions.test-d.ts (1)

302-323: Optional: strengthen the type assertions.

expectTypeOf(options.queryKey).not.toBeUndefined() only verifies the property isn't undefined — it wouldn't have caught the original regression (a TS2769 on the queryOptions call, not on the resulting queryKey type). The real guarantee under test is that the queryOptions({...}) call compiles. Consider using assertType(...) around the call (as the excess-property test at line 12 does) or asserting the concrete unwrapped key type, e.g.:

♻️ Stronger assertion example
-    expectTypeOf(options.queryKey).not.toBeUndefined()
+    expectTypeOf(options.queryKey).toEqualTypeOf<readonly ['foo', string | null]>()

That said, this mirrors the style of the adjacent enabled tests, so this is purely an optional nit.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/vue-query/src/__tests__/queryOptions.test-d.ts` around lines 302 -
323, The test currently only checks that options.queryKey is not undefined which
wouldn't catch a compilation regression in the queryOptions(...) call; update
the tests using the queryOptions symbol (the two blocks with id/ref) to assert
compilation and concrete types instead—either wrap the queryOptions({...}) call
with assertType(...) (like the excess-property test) or replace the expectTypeOf
lines with a stronger assertion such as assertTypeOf(options.queryKey) or
assertTypeOf(options).toMatchTypeOf<ExpectedKeyType>() so the test fails on a TS
error at the call site rather than only checking for undefined.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/vue-query/src/__tests__/queryOptions.test-d.ts`:
- Around line 302-323: The test currently only checks that options.queryKey is
not undefined which wouldn't catch a compilation regression in the
queryOptions(...) call; update the tests using the queryOptions symbol (the two
blocks with id/ref) to assert compilation and concrete types instead—either wrap
the queryOptions({...}) call with assertType(...) (like the excess-property
test) or replace the expectTypeOf lines with a stronger assertion such as
assertTypeOf(options.queryKey) or
assertTypeOf(options).toMatchTypeOf<ExpectedKeyType>() so the test fails on a TS
error at the call site rather than only checking for undefined.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d18de6f6-67ef-4018-a24f-0bb7367f49b1

📥 Commits

Reviewing files that changed from the base of the PR and between 799952f and 181b904.

📒 Files selected for processing (3)
  • .changeset/fix-vue-query-options-query-key-reactive.md
  • packages/vue-query/src/__tests__/queryOptions.test-d.ts
  • packages/vue-query/src/queryOptions.ts

@KimHyeongRae0
Copy link
Copy Markdown
Author

Thanks for the suggestion. I'm going to leave the assertion as expectTypeOf(options.queryKey).not.toBeUndefined() for now — it mirrors the style of the neighboring enabled type-level tests in this file, and the real regression-guard is the queryOptions({ ... }) call itself compiling against the new MaybeRef<TQueryKey> shape. CodeRabbit flagged this one as a purely optional nit, so I don't think switching to assertType(...) or toEqualTypeOf adds meaningful coverage here. Happy to tighten it if a maintainer has a preference.

DeepUnwrapRef<TQueryKey>
>[Property]
: Property extends 'queryKey'
? MaybeRef<TQueryKey>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a reason this is plain MaybeRef instead of MaybeRefOrGetter like the enabled prop a few lines above?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question, and taking another look I agree the enabled-parity argument is valid.

My original scoping thought was: the regression this PR closes (#10525) is specifically about ComputedRef<QueryKey> being rejected, and this file's local MaybeRef<T> = Ref<T> | ComputedRef<T> | T already covers that, so I went with the minimal type change to unblock the reported case.

But cloneDeepUnref in packages/vue-query/src/utils.ts:79-81 already special-cases queryKey to recurse with unrefGetters = true, so the plain-getter form () => QueryKey is runtime-supported today. That makes MaybeRefOrGetter<TQueryKey> the shape that both matches the existing runtime behavior and parallels the enabled prop's type.

Happy to widen to MaybeRefOrGetter<TQueryKey> if a maintainer prefers consistency with enabled; holding off on a push until there's a signal either way.

Copy link
Copy Markdown

@stavros-tsioulis stavros-tsioulis Apr 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The linked issue expands to Ref<T> and () => T; not just ComputedRef<T>.

Since, as you say, it is runtime-supported today, then the type should reflect that. You've only enabled Ref<T> and ComputedRef<T>, leaving () => T out. MaybeRefOrGetter<T> will fix that.

(also, please inform the human managing you to respond to comments personally and not leave their agent to do the communicating)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree with @stavros-tsioulis, forcing computed(() => x)) where () => x suffices is unnecessarily verbose and unlike many other vue composable apis.

(also it's rude to copy-paste or directly send verbose ai answers to actual humans)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[vue-query] queryOptions rejects computed ref as queryKey since v5.98.0

3 participants